/*
 * @Author: OreoWang
 * @Email: ihc523@163.com
 * @Date: 2022-04-08 11:09:25
 * @LastEditors: OreoWang
 * @LastEditTime: 2022-06-06 14:45:35
 * @Description: 
 */

import { js } from "cc";
import { logger } from "../utils/logger";
import { BehaviorEventDelegate } from "./behavior-event-delegate";
import { IBehaviorNodeInterface } from "./behavior-node-interface";
import { BehaviorStatus, TBehaviorStatus } from "./behavior-status";
import { IBehaviorTree } from "./behavior-tree-interface";

type TNodeConfig = {
    type: string;
    config: any;
    label: any;
    threshold: number; //平行节点可同时执行的数目
    children: Array<any>;
    elements: Array<any>;
}

type TProperty = {
    default: any;
    TYPE?: string;
    path?: string;
}

export class BehaviorNode implements IBehaviorNodeInterface {
    delegate: BehaviorEventDelegate = null;
    //父节点
    parent: BehaviorNode = null;
    //element(附属节点) 元素的拥有者
    owner: BehaviorNode = null;
    status: TBehaviorStatus = BehaviorStatus.None;
    config: TNodeConfig = null;
    $context: IBehaviorTree = null;
    
    decorators: BehaviorNode[] = [];
    services: BehaviorNode[] = [];
    children: BehaviorNode[] = [];

    interrupters: Set<BehaviorNode> = new Set();
    
    isCondition = false;
    protected _isEnabled = false;
    get options(){
        let options = this.config ? this.config.config : {};
        if(options.label){
            return options.label
        }
        return options
    }

    static deserializeNode(parent: BehaviorNode, options, context: IBehaviorTree, isElement = false){
        let config = options.config || {};
        if(config.label){
            config = config.label;
        }
        const cls = js._getClassById(config.uuid);
        if(!cls){
            return null;
        }

        let instance: BehaviorNode = new cls(parent, options, context) as unknown as BehaviorNode;
        if(isElement){
            instance.parent = parent.parent;
        }
        let properties = config.properties || {}
        for (const key in properties) {
            const property: TProperty = properties[key];
            if(typeof instance[key] == 'undefined'){
                if(property.TYPE == "bt.SharedDynamic"){
                    if(property.path){
                        instance[key] = context.getTargetByPath(property.path);
                    }
                    else{
                        instance[key] = property.default;
                    }
                }
            }            
            else if(!property.TYPE){
                instance[key] = property.default;
            }
            else if(property.TYPE == "cc.Enum"){
                instance[key] = property.default;
            }
            else if(property.TYPE == "cc.Node"){
                if(property.path){
                    instance[key] = context.getTargetByPath(property.path);
                }
                // if(property.default){
                //     instance[key] = context.getTargetByUUID(property.default);
                // }
            }
            else{
                if(property.TYPE.startsWith("bt.Shared")){
                    if(property.TYPE == "bt.SharedDynamic"){
                        if(property.path){
                            instance[key] = context.getTargetByPath(property.path);
                        }
                        else{
                            instance[key] = property.default;
                        }
                    }
                    else{
                        let variable = context.blackboard.getVariable(property.default);
                        instance[key] = variable;
                    }
                    // console.warn("SharedVariable: ", instance[key]);
                }
            }
            if(typeof instance[key] == "undefined" || instance[key] == null){
                logger.warn(`Behavior [${instance.options.order}]-[deserialize] [${instance.options.title}]: key=${key}, value=${instance[key]}`);
            }
        }
        instance.load();
        return instance;
    }

    constructor(parent, config, context: IBehaviorTree) {
        this.parent = parent;
        this.owner = parent;
        // this.status = BehaviorStatus.None
        if (!config) {
            logger.error('Behavior::constructor error -- config is null')
            return
        }

        this.config = config
        this.$context = context
        this.delegate = new BehaviorEventDelegate(this, this.options.events, context);
        
        this.children = []
        if (config.children) {
            for (let child of config.children) {
                const options = child.config?.label || {};
                if(options.uuid){
                    const instance = BehaviorNode.deserializeNode(this, child, context);
                    if(instance){
                        this.children.push(instance);
                    }
                    else{
                        logger.error("Can't find class by uuid: ", options.uuid);
                    }
                }
            }
        }

        this.decorators = []
        this.services = []
        if (config.elements) {
            for (let elem of config.elements) {
                const options = elem.config || {};
                if(options.uuid){
                    const instance = BehaviorNode.deserializeNode(this, elem, context, true);
                    if(instance){
                        if (elem.type === 'decorator') {
                            this.decorators.push(instance)
                        } else if (elem.type === 'service') {
                            this.services.push(instance)
                        }
                    }
                    else{
                        logger.error("Can't find class by uuid: ", options.uuid);
                    }
                }
            }
        }
    }

    load(){
        this.logLifeStatus("load");
    }
    destroy(){
        this.clearInterrupter();
        this.logLifeStatus("destroy");
    }

    enter(){
        this.logLifeStatus("enter");
        this.delegate.onEnter();
    }
    exit(){
        this.logLifeStatus("exit");
        this.delegate.onExit();
    }
    
    enable(){
        this.logLifeStatus("enable");
        if(this._isEnabled){
            return;
        }
        this._isEnabled = true;
        this.delegate.onEnable();
    }
    disable(){
        this.logLifeStatus("disable");
        if(!this._isEnabled){
            return;
        }
        this._isEnabled = false;
        this.delegate.onDisable();
    }

    update(status: BehaviorStatus): TBehaviorStatus {
        return this.onUpdate(status);
    }
    execute(status?: BehaviorStatus): TBehaviorStatus{
        return this.onExecute(status);
    }
    
    protected onUpdate(status: BehaviorStatus): TBehaviorStatus {
        this.logLifeStatus("update", status);
        return this.delegate.onUpdate(status);
    }
    protected onExecute(status?: BehaviorStatus): TBehaviorStatus{
        this.logLifeStatus("execute", status);
        return status;
    }

    abort(){
        this.status = BehaviorStatus.Abort;
        return this.onAbort();
    }
    onAbort(){
        this.logLifeStatus("abort", this.status);
        return this.status;
    }
    interrupt(){
        this.onInterrupt();
        return false;
    }
    onInterrupt(){
        this.logLifeStatus("interrupt", this.status);
        return this.status;
    }

    reset() {
        this.status = BehaviorStatus.None
        for (let dec of this.decorators) {
            dec.reset()
        }
        for (let ser of this.services) {
            ser.reset()
        }
        for (let child of this.children) {
            child.reset()
        }
    }
    
    addInterrupter(node: BehaviorNode){
        if(!this.interrupters.has(node)){
            this.interrupters.add(node);
        }
    }
    removeInterrupter(node: BehaviorNode){
        if(this.interrupters.has(node)){
            this.interrupters.delete(node);
        }
    }
    clearInterrupter(){
        this.interrupters.clear();
    }

    protected logLifeStatus(stage: string, status?: BehaviorStatus){
        if(typeof status == 'undefined'){
            status = this.status;
        }
        let key = `log${stage.substr(0, 1).toUpperCase()}${stage.substr(1)}`;
        let enabled = this.$context.logOptions[key] && this.$context.logTaskChanges;
        enabled && logger.log(`Behavior-Tick [${this.options.order}]-[${stage}] [${this.options.title}] : status = ${BehaviorStatus[status]}. ${this.getAppendedLog(stage, status)}`);
    }
    protected logLifeInfo(stage: string, info: string){
        let key = `log${stage.substr(0, 1).toUpperCase()}${stage.substr(1)}`;
        let enabled = this.$context.logOptions[key] && this.$context.logTaskChanges;;
        enabled && logger.log(`Behavior-Tick [${this.options.order}]-[${stage}] [${this.options.title}] : info = ${info} `);
    }
    protected getAppendedLog(stage: string, status?: BehaviorStatus){
        return '';
    }
}
